﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Linq;
using System.Windows.Controls.Primitives;

namespace Gmantis.Controls.DataGrid
{
    public class DataGridColumnCollection : Collection<DataGridColumn>, INotifyCollectionChanged
    {
        // Fields
        private bool _isOrderedColumnsDirty = true;
        private DataGridModel _model;
        private DataGridColumn[] _orderedColumns;

        // Events
        internal event EventHandler<DataGridColumnEventArgs> AfterAddColumn;
        internal event EventHandler<DataGridColumnEventArgs> AfterRemoveColumn;
        internal event EventHandler<DataGridColumnEventArgs> BeforeAddColumn;
        internal event EventHandler<DataGridColumnEventArgs> BeforeRemoveColumn;
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        internal event EventHandler<DataGridColumnEventArgs> ColumnReordered;
        internal event EventHandler<DataGridColumnEventArgs> ColumnVisibilityChanged;

        // Methods
        internal DataGridColumnCollection(DataGridModel model)
        {
            this._model = model;
        }

        internal void AddAutogenerated(DataGridColumn item)
        {
            this.InsertItem(base.Count, item);
        }

        internal void ClearAutogeneratedColumns()
        {
            for (int i = base.Count - 1; i >= 0; i--)
            {
                DataGridColumn column = this[i];
                if (column.IsAutoGenerated)
                {
                    this.OnBeforeRemoveColumn(column);
                    base.RemoveItem(i);
                    this.OnAfterRemoveColumn(column);
                }
            }
        }

        protected override void ClearItems()
        {
            for (int i = base.Count - 1; i >= 0; i--)
            {
                DataGridColumn item = this[i];
                base.Remove(item);
            }
        }

        private void column_VisibilityChanged(object sender, PropertyChangedEventArgs<Visibility> e)
        {
            DataGridColumn column = (DataGridColumn)sender;
            this.OnColumnVisibilityChanged(column);
        }

        public bool Contains(string columnName)
        {
            foreach (DataGridColumn column in this)
            {
                if (column.Name == columnName)
                {
                    return true;
                }
            }
            return false;
        }

        public DataGridColumn GetFirstVisibleColumn()
        {
            return this.GetVisibleColumnsFromLeft().FirstOrDefault<DataGridColumn>();
        }

        public DataGridColumn GetLastVisibleColumn()
        {
            return this.GetVisibleColumnsFromRight().FirstOrDefault<DataGridColumn>();
        }

        [Obsolete("Use GetVisibleColumnsFromIndex() instead.")]
        public DataGridColumn GetNextVisibleColumn(int columnIndex)
        {
            List<DataGridColumn> list = this.GetVisibleColumnsFromIndex(columnIndex, GeneratorDirection.Forward).Take<DataGridColumn>(2).ToList<DataGridColumn>();
            if (list.Count == 2)
            {
                return list[1];
            }
            return null;
        }

        [Obsolete("Use GetVisibleColumnsFromIndex() instead.")]
        public DataGridColumn GetPreviousVisibleColumn(int columnIndex)
        {
            List<DataGridColumn> list = this.GetVisibleColumnsFromIndex(columnIndex, GeneratorDirection.Backward).Take<DataGridColumn>(2).ToList<DataGridColumn>();
            if (list.Count == 2)
            {
                return list[1];
            }
            return null;
        }

        public IEnumerable<DataGridColumn> GetVisibleColumnsFromIndex(int fromColumnIndex, GeneratorDirection direction)
        {
            if (((this.OrderedColumns.Length != 0) && ((fromColumnIndex >= 0) || (direction != GeneratorDirection.Backward))) && ((fromColumnIndex <= (this.OrderedColumns.Length - 1)) || (direction != GeneratorDirection.Forward)))
            {
                int index = Math.Max(0, Math.Min(this.OrderedColumns.Length - 1, fromColumnIndex));
                do
                {
                    DataGridColumn iteratorVariable1 = this.OrderedColumns[index];
                    if (iteratorVariable1.Visibility == Visibility.Visible)
                    {
                        yield return iteratorVariable1;
                    }
                    index += (direction == GeneratorDirection.Forward) ? 1 : -1;
                }
                while ((index >= 0) && (index < this.OrderedColumns.Length));
            }
        }

        public IEnumerable<DataGridColumn> GetVisibleColumnsFromLeft()
        {
            return this.GetVisibleColumnsFromIndex(0, GeneratorDirection.Forward);
        }

        public IEnumerable<DataGridColumn> GetVisibleColumnsFromRight()
        {
            return this.GetVisibleColumnsFromIndex(base.Count - 1, GeneratorDirection.Backward);
        }

        protected override void InsertItem(int index, DataGridColumn item)
        {
            this.OnBeforeAddColumn(item);
            base.InsertItem(index, item);
            this.OnAfterAddColumn(item);
        }

        internal void InvalidateOrderedColumns()
        {
            this._isOrderedColumnsDirty = true;
        }

        internal void MoveColumn(DataGridColumn movedColumn, int toColumnDisplayIndex)
        {
            DataGridColumn[] orderedColumns = this.Model.Columns.OrderedColumns;
            if ((toColumnDisplayIndex >= orderedColumns.Length) || (toColumnDisplayIndex < 0))
            {
                throw new ArgumentException("Parameter toColumnDisplayIndex must be between 0 and VisibleColumns.Count.");
            }
            int calculatedDisplayIndex = movedColumn.CalculatedDisplayIndex;
            int num2 = orderedColumns[toColumnDisplayIndex].CalculatedDisplayIndex;
            foreach (DataGridColumn column in this)
            {
                int num3 = column.CalculatedDisplayIndex;
                if (column != movedColumn)
                {
                    if (calculatedDisplayIndex < num2)
                    {
                        if ((num3 > calculatedDisplayIndex) && (num3 <= num2))
                        {
                            column.CalculatedDisplayIndex--;
                        }
                    }
                    else if ((num3 >= num2) && (num3 < calculatedDisplayIndex))
                    {
                        column.CalculatedDisplayIndex++;
                    }
                }
            }
            movedColumn.CalculatedDisplayIndex = num2;
            this.InvalidateOrderedColumns();
            this.OnColumnsReordered(movedColumn);
            this.Model.DataGrid.OnColumnReordered(new DataGridColumnEventArgs(movedColumn));
        }

        private void OnAfterAddColumn(DataGridColumn column)
        {
            this.OnColumnAdded(column);
            this.InvalidateOrderedColumns();
            if (this.AfterAddColumn != null)
            {
                this.AfterAddColumn(this, new DataGridColumnEventArgs(column));
            }
            this.OnCollectionChanged(NotifyCollectionChangedAction.Add, column, column.DisplayIndex);
        }

        private void OnAfterRemoveColumn(DataGridColumn column)
        {
            this.OnColumnRemoved(column);
            column.VisibilityChanged -= new EventHandler<PropertyChangedEventArgs<Visibility>>(this.column_VisibilityChanged);
            if (this.AfterRemoveColumn != null)
            {
                this.AfterRemoveColumn(this, new DataGridColumnEventArgs(column));
            }
            this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, column, column.DisplayIndex);
        }

        private void OnBeforeAddColumn(DataGridColumn column)
        {
            column.VisibilityChanged += new EventHandler<PropertyChangedEventArgs<Visibility>>(this.column_VisibilityChanged);
            if (this.BeforeAddColumn != null)
            {
                this.BeforeAddColumn(this, new DataGridColumnEventArgs(column));
            }
        }

        private void OnBeforeRemoveColumn(DataGridColumn column)
        {
            if (this.BeforeRemoveColumn != null)
            {
                this.BeforeRemoveColumn(this, new DataGridColumnEventArgs(column));
            }
        }

        private void OnCollectionChanged(NotifyCollectionChangedAction action, DataGridColumn column, int index)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, column, index));
            }
        }

        private void OnColumnAdded(DataGridColumn column)
        {
            int num = -1;
            foreach (DataGridColumn column2 in this)
            {
                if (column2 != column)
                {
                    num = Math.Max(num, column2.CalculatedDisplayIndex);
                }
            }
            column.CalculatedDisplayIndex = num + 1;
            this.InvalidateOrderedColumns();
        }

        private void OnColumnRemoved(DataGridColumn column)
        {
            int calculatedDisplayIndex = column.CalculatedDisplayIndex;
            foreach (DataGridColumn column2 in this)
            {
                if ((column2 != column) && (column2.CalculatedDisplayIndex >= calculatedDisplayIndex))
                {
                    column2.CalculatedDisplayIndex--;
                }
            }
            this.InvalidateOrderedColumns();
        }

        private void OnColumnsReordered(DataGridColumn column)
        {
            if (this.ColumnReordered != null)
            {
                this.ColumnReordered(this, new DataGridColumnEventArgs(column));
            }
        }

        private void OnColumnVisibilityChanged(DataGridColumn column)
        {
            if (this.ColumnVisibilityChanged != null)
            {
                this.ColumnVisibilityChanged(this, new DataGridColumnEventArgs(column));
            }
        }

        protected override void RemoveItem(int index)
        {
            DataGridColumn column = this[index];
            this.OnBeforeRemoveColumn(column);
            base.RemoveItem(index);
            this.OnAfterRemoveColumn(column);
        }

        protected override void SetItem(int index, DataGridColumn item)
        {
            base.RemoveAt(index);
            this.InsertItem(index, item);
        }

        // Properties
        public DataGridColumn this[int columnIndex]
        {
            get
            {
                return base[columnIndex];
            }
        }

        public DataGridColumn this[string columnName]
        {
            get
            {
                return this.FirstOrDefault<DataGridColumn>(column => (column.Name == columnName));
            }
        }

        private DataGridModel Model
        {
            get
            {
                return this._model;
            }
        }

        internal DataGridColumn[] OrderedColumns
        {
            get
            {
                if (this._isOrderedColumnsDirty)
                {
                    this._isOrderedColumnsDirty = false;
                    this._orderedColumns = (from column in this
                                            orderby column.DisplayIndex
                                            select column).ToArray<DataGridColumn>();
                }
                return this._orderedColumns;
            }
        }
    }
}
